package com.hzw.net.net

import com.hzw.core.log.logE
import com.hzw.core.log.logI
import com.hzw.net.BuildConfig
import com.hzw.net.annotation.BaseUrl
import com.hzw.net.annotation.HttpLog
import com.hzw.net.annotation.NoEncrypt
import com.hzw.net.annotation.NoToken
import com.hzw.net.manager.HttpManager
import com.hzw.net.manager.TimeManager
import com.hzw.net.manager.TokenManager
import com.hzw.net.net.exception.Errors
import com.hzw.net.net.exception.ResourceException
import com.hzw.net.net.json.GsonClient
import com.hzw.net.net.resource.Http
import com.hzw.net.utils.AESUtil
import okhttp3.FormBody
import okhttp3.OkHttpClient
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.logging.HttpLoggingInterceptor
import okio.Buffer
import okio.IOException
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.GET
import retrofit2.http.POST
import java.lang.IllegalArgumentException
import java.util.concurrent.TimeUnit

/**
 *
 * @Author:         HuaZhongWei
 * @CreateDate:     2020/11/2 11:21
 *
 * @Description:
 *
 */
object HttpCreator {

    fun create(
        clazz: Class<*>,
        isDebug: Boolean = true,
        headers: List<Pair<String, String>>? = null,
        encrypt: ((String) -> String)? = {
            val token = TokenManager.getToken()
            if (token.isEmpty()) {
                it
            } else {
                AESUtil.encrypt(it, token)
            }
        }
    ): Http {
        val http = Http()
        parseApiClass(clazz, http)
        if (!headers.isNullOrEmpty()) {
            headers.forEach {
                http.headers.add(it)
            }
        }
        createRetrofit(
            http = http,
            isDebug = isDebug,
            encrypt = encrypt
        )
        HttpManager.addHttp(http.baseUrl, http)
        return http
    }

    /**
     * 解析Api文件
     */
    fun parseApiClass(clazz: Class<*>, http: Http) {
        val baseUrlAnnotation = parseApiBaseUrl(clazz)
        http.httpLog = baseUrlAnnotation.log
        http.baseUrl = baseUrlAnnotation.url
        val methods = clazz.declaredMethods
        methods.forEach { method ->
            val noToken = method.getAnnotation(NoToken::class.java)
            val noEncrypt = method.getAnnotation(NoEncrypt::class.java)
            val post = method.getAnnotation(POST::class.java)
            val get = method.getAnnotation(GET::class.java)

            if (noToken != null) {
                post?.let { http.noTokenSet.add(it.value) }
                get?.let { http.noTokenSet.add(it.value) }
            }

            // get不考虑加密，只有post才考虑加密
            if (noEncrypt != null && post != null) {
                http.noEncryptSet.add(post.value)
            }
        }
    }

    private fun parseApiBaseUrl(clazz: Class<*>): BaseUrl {
        val baseUrl = clazz.getAnnotation(BaseUrl::class.java)
        if (baseUrl == null || baseUrl.url.isEmpty()) {
            throw  IllegalArgumentException("Api接口，必须有BaseUrl注解并且url不能为空")
        }
        return baseUrl
    }

    fun parseApiUrl(clazz: Class<*>): String = parseApiBaseUrl(clazz).url

    /**
     * 判断集合中是否存在指定url
     * 集合中的url需要和baseUrl拼接成完整的url
     */
    private fun isContains(set: Set<String>, baseUrl: String, url: String): Boolean {
        if (set.isEmpty()) {
            return false
        }
        set.forEach {
            if (baseUrl + it == url) {
                return true
            }
        }
        return false
    }

    /**
     * 创建 retrofit
     */
    private fun createRetrofit(
        http: Http,
        isDebug: Boolean = true,
        encrypt: ((String) -> String)? = null
    ) {
        try {
            // 日志等级
            // debug下，非上传和下载打印所有信息，否则打印基本信息
            val level = if (isDebug) {
                when(http.httpLog) {
                    HttpLog.HTTP_LOG_NORMAL -> HttpLoggingInterceptor.Level.BODY
                    HttpLog.HTTP_LOG_BASIC -> HttpLoggingInterceptor.Level.BASIC
                    HttpLog.HTTP_LOG_NOTHING -> HttpLoggingInterceptor.Level.NONE
                }
            } else {
                HttpLoggingInterceptor.Level.NONE
            }

            val clientBuilder = OkHttpClient.Builder()
                .writeTimeout(ApiConstant.DEFAULT_HTTP_TIMEOUT, TimeUnit.MILLISECONDS)
                .connectTimeout(ApiConstant.DEFAULT_HTTP_TIMEOUT, TimeUnit.MILLISECONDS)
                .readTimeout(ApiConstant.DEFAULT_HTTP_TIMEOUT, TimeUnit.MILLISECONDS)
                .addNetworkInterceptor(HttpLoggingInterceptor().setLevel(level))

            if (http.headers.isNotEmpty()) {
                //处理公共header的拦截器
                clientBuilder.addInterceptor { chain ->
                    try {
                        var request = chain.request()
                        val noTokenSet = http.noTokenSet
                        //解决并发操作
                        synchronized(HttpCreator::class.java) {
                            val newBuilder = request.headers.newBuilder()
                            http.headers.forEach { header ->
                                newBuilder.add(header.first, header.second)
                            }
                            newBuilder.add(ApiConstant.HEADER_TIMESTAMP, TimeManager.getLocalTime().toString())
                            if (!isContains(noTokenSet, http.baseUrl, request.url.toString())) {
                                newBuilder.add(ApiConstant.HEADER_TOKEN, TokenManager.encryptToken())
                            }
                            request = request.newBuilder().headers(newBuilder.build()).build()
                            chain.proceed(request)
                        }
                    } catch (e: Exception) {
                        throw IOException(Errors.NETWORK_ERROR.msg)
                    }
                }
            }

            if (encrypt != null) {
                // 处理加密的拦截器
                clientBuilder.addInterceptor { chain ->
                    try {
                        var request = chain.request()
                        val noEncryptSet = http.noEncryptSet
                        if (request.method == "POST" && request.body != null && request.body !is FormBody && !isContains(
                                noEncryptSet,
                                http.baseUrl,
                                request.url.toString()
                            )
                        ) {
                            val buffer = Buffer()
                            request.body?.writeTo(buffer)
                            val oldData = buffer.readUtf8()
                            if (isDebug && oldData.isNotEmpty()) {
                                "接口(${request.url}) \n数据 ==> $oldData".logI()
                            }
                            val newData = encrypt(oldData)
                            if (isDebug && newData.isNotEmpty()) {
                                "接口${request.url} \n数据 ==> $newData".logI()
                            }
                            val requestBody = newData.toRequestBody(request.body?.contentType())
                            request = request.newBuilder().method(request.method, requestBody).build()
                        }
                        chain.proceed(request)
                    } catch (e: Exception) {
                        throw IOException(Errors.NETWORK_ERROR.msg)
                    }
                }
            }

            if (http.baseUrl.isEmpty()) {
                throw ResourceException(Errors.IP_ERROR)
            }

            http.retrofit = Retrofit.Builder()
                .baseUrl(http.baseUrl)
                .client(clientBuilder.build())
                .addConverterFactory(GsonConverterFactory.create(GsonClient.create()))
                .build()
        } catch (e: Exception) {
            e.logE()
        }
    }
}